-- AvalonFortM
-- Create by dengc
-- 阿瓦隆要塞模块

module("AvalonFortM", package.seeall);

-- 初始化
function init()
    -- 关注回合数变化
    CombatM.registerStartRoundCB("avalon_fort_start_round", whenCombatRound);

    EventMgr.removeAll("AvalonFortM");

    -- 关注开始地牢事件
    EventMgr.register("AvalonFortM", event.DUNGEON_PREPARE_OVER, function()
        whenStartDungeon();
    end);

    -- 注册玩家复活的回调
    EventMgr.register("AvalonFortM", event.HERO_REVIVE, function()
        whenUserRevive();
    end);
end

-- 回合数变化
whenCombatRound = function()
    -- 尝试爆炸定时炸弹
    tryBomb();
end

-- 进入地牢
whenStartDungeon = function()
    -- 毒气装置赋予DEBUF
    gasDeviceApplyDebuf();

    -- 钢铁之门赋予怪物属性
    steelDoorApplyDebuf();
end

-- 玩家复活
whenUserRevive = function()
    -- 毒气装置赋予DEBUF
    gasDeviceApplyDebuf();
end

-- 找到飞艇控制台
function findAirshipConsole()
    -- 遍历所有格子
    local grids = DungeonM.getCurrentDungeon() or {};
    for _, grid in pairs(grids) do
        if grid.type == GRID_TYPE_AIRSHIP_CONSOLE then
            return grid;
        end
    end

    return nil;
end

-- 找到阿瓦隆巨炮
function findAvalonCannon()
    -- 遍历所有格子
    local grids = DungeonM.getCurrentDungeon() or {};
    for _, grid in pairs(grids) do
        if grid.type == GRID_TYPE_AVALON_CANNON then
            return grid.avalon_cannon;
        end
    end

    return nil;
end

-- 计算定时炸弹的拆除线路
function calcTimeBombVitaport()
    local info = FormulaM.invoke("CALC_TIME_BOMB_VITAPORT");

    return {["bomb_vitaport"] = info,};
end

-- 尝试拆除定时炸弹
function tryRemoveTimeBomb(pos, value)
    -- 当前格子已经无效了
    local grid = DungeonM.getGridByPos(pos);
    if grid.state == GRID_STATE_DISABLE then
        return false;
    end

    if grid.type ~= GRID_TYPE_TIMEBOMB then
        return false;
    end

    -- 获取选择的颜色
    local info = grid.bomb_vitaport;
    if not info or type(info) ~= "table" then
        return false;
    end

    local color = info[value];

    -- 获取正确的颜色
    local rightColor = grid.right_color;
    if not rightColor then
        return false;
    end

    local isSuccess = false;
    if rightColor == color then
        isSuccess = true;
    end

    -- 成功拆除
    local bonus;
    if isSuccess then
        -- 计算奖励
        bonus = FormulaM.invoke("CALC_AVALON_BUILDING_BONUS", grid.type);

        -- 奖励
        BonusM.doBonus(bonus, "avalon_building");
    else
        -- 失败，爆炸
        local skillId = grid.skill;
        SkillM.apply(ME.user, ME.user, skillId, CombatM.getRound());
    end

    -- 把格子设置为无效
    grid:changeState(GRID_STATE_DISABLE);

    -- 抛出定时炸弹拆除事件
    EventMgr.fire(event.REMOVE_TIME_BOMB, {["isSuccess"] = isSuccess, ["pos"] = pos, ["bonus"] = bonus,});

    return true;
end

-- 找到定时炸弹
function findTimeBomb()
    -- 遍历所有格子
    local grids = DungeonM.getCurrentDungeon() or {};
    for _, grid in pairs(grids) do
        if grid.type == GRID_TYPE_TIMEBOMB then
            return grid;
        end
    end

    return nil;
end

-- 尝试爆炸定时炸弹
function tryBomb()
    -- 找到定时炸弹
    local grid = findTimeBomb();
    if not grid then
        return;
    end

    -- 当前格子已经无效了
    if grid.state == GRID_STATE_DISABLE then
        return;
    end

    -- 当前回合大于爆炸回合才能爆炸
    local round = CombatM.getRound();
    local boomRound = grid.boom_round or 0;
    if round < boomRound then
        -- 抛出刷新定时炸弹剩余时间的事件
        EventMgr.fire(event.UPDATE_TIME_BOMB_TIME, {["pos"] = grid:getPos(),});

        return;
    end

    -- 释放技能
    local skillId = grid.skill;
    SkillM.apply(ME.user, ME.user, skillId, round);

    -- 把格子设置为无效
    grid:changeState(GRID_STATE_DISABLE);

    -- 抛出定时炸弹拆除失败事件
    EventMgr.fire(event.REMOVE_TIME_BOMB, {["isSuccess"] = false, ["pos"] = grid:getPos(),});
end

-- 获取定时炸弹的爆炸时间
function getBombTime(gridData)
    local boomRound = gridData.boom_round;
    if type(boomRound) ~= 'number' then
        return 1;
    else
        return boomRound - CombatM.getRound() + 1;
    end
end

-- 找到毒气装置
function findGsaDevice()
    -- 遍历所有格子
    local grids = DungeonM.getCurrentDungeon() or {};
    for _, grid in pairs(grids) do
        if grid.type == GRID_TYPE_GAS_DEVICE then
            return grid;
        end
    end

    return nil;
end

-- 毒气装置，赋予debuf状态
function gasDeviceApplyDebuf()
    -- 找到毒气装置
    local grid = findGsaDevice();
    if not grid then
        return;
    end

    -- 当前格子已经无效了
    if grid.state == GRID_STATE_DISABLE then
        return;
    end

    -- 获取状态配置
    local status = grid.apply_status;
    if not status then
        return;
    end

    -- 如果玩家免疫中毒，不赋予
    if CombatStatusM.immunityStatus(ME.user, "poisoned") then
        return;
    end

    -- 赋予状态
    local condition = table.deepcopy(status);
    condition.status = nil;

    CombatStatusM.applyStatus(ME.user, status["status"], condition);

    -- 处理中毒触发
    local props = PropM.fetchProps(ME.user, "conversion_toxin");
    for _, prop in ipairs(props) do
        PropM.trigger(ME.user, prop[1], prop[2]);
    end

    props = PropM.fetchProps(ME.user, "poisoning_to_attrib");
    for _, prop in ipairs(props) do
        PropM.trigger(ME.user, prop[1], prop[2]);
    end

    props = PropM.fetchProps(ME.user, "poisoning_to_status");
    for _, prop in ipairs(props) do
        PropM.trigger(ME.user, prop[1], prop[2], {["no_cost"] = true,});
    end
end

-- 摧毁毒气装置
function destoryGasDevice(pos)
    local grid = DungeonM.getGridByPos(pos);
    if not grid then
        return false;
    end

    -- 当前格子已经无效了
    if grid.state == GRID_STATE_DISABLE then
        return false;
    end

    if grid.type ~= GRID_TYPE_GAS_DEVICE then
        return false;
    end

    -- 获取debuf配置
    local status = grid.apply_status;
    if not status or type(status) ~= "table" then
        return false;
    end

    -- 清除DEBUF
    CombatStatusM.clearStatus(ME.user, status["status"]);

    -- 计算奖励
    bonus = FormulaM.invoke("CALC_AVALON_BUILDING_BONUS", grid.type);

    -- 奖励
    BonusM.doBonus(bonus, "avalon_building");

    -- 把格子设置为失效
    grid:changeState(GRID_STATE_DISABLE);

    -- 抛出摧毁毒气装置事件
    EventMgr.fire(event.DESTORY_GAS_DEVICE, {["pos"] = pos, ["bonus"] = bonus,});

    -- 通知建筑事件完成
    EventMgr.fire(event.BUILDING_EVENT_DONE, {["pos"] = pos,});

    return true;
end

-- 找到钢铁之门
function findSteelDoor()
    -- 遍历所有格子
    local grids = DungeonM.getCurrentDungeon() or {};
    for _, grid in pairs(grids) do
        if grid.type == GRID_TYPE_STEEL_DOOR then
            return grid;
        end
    end

    return nil;
end

-- 钢铁之门，赋予怪物增益状态
function steelDoorApplyDebuf()
    -- 找到钢铁之门
    local grid = findSteelDoor();
    if not grid then
        return;
    end

    -- 当前格子已经无效了
    if grid.state == GRID_STATE_DISABLE then
        return;
    end

    -- 获取buf配置
    local props = grid.apply_props;
    if not props then
        return;
    end

    -- 找到全场的怪物，赋予属性
    local monster;
    local grids = DungeonM.getCurrentDungeon() or {};
    for _, g in pairs(grids) do
        repeat
            monster = g.monster;
            if not monster then
                break;
            end

            if monster:isDead() then
                break;
            end

            -- 先删除
            PropM.deleteProp(monster, "building_assist");

            for _, prop in ipairs(props) do
                PropM.record(monster, prop, "building_assist");
            end
        until true;
    end
end

-- 摧毁钢铁之门
function destorySteelDoor(pos)
    local grid = DungeonM.getGridByPos(pos);
    if not grid then
        return false;
    end

    -- 当前格子已经无效了
    if grid.state == GRID_STATE_DISABLE then
        return false;
    end

    if grid.type ~= GRID_TYPE_STEEL_DOOR then
        return false;
    end

    -- 找到全场的怪物，清除钢铁之门赋予的属性
    local monster;
    local grids = DungeonM.getCurrentDungeon() or {};
    for _, grid in pairs(grids) do
        repeat
            monster = grid.monster;
            if not monster then
                break;
            end

            if monster:isDead() then
                break;
            end

            PropM.deleteProp(monster, "building_assist");
        until true;
    end

    -- 抛出钢铁之门附加减益事件
    EventMgr.fire(event.UI_STEEL_DOOR_ADD_DEBUF);

    -- 计算奖励
    bonus = FormulaM.invoke("CALC_AVALON_BUILDING_BONUS", grid.type);

    -- 奖励
    BonusM.doBonus(bonus, "avalon_building");

    -- 计算转化为钢铁之门废墟的概率
    local ratio = DungeonM.getRandSeed("CALC_TRANS_GRID_RATIO") % 1000;

    -- 获取配置
    local config = grid.trans_grid;
    local ret = config["ratio"];

    -- 转化为钢铁之门废墟
    if ratio < ret then
        local elementId = config["element"];
        local class = DungeonM.queryElement(elementId, "class");
        local para = {["class"] = class, ["element"] = elementId,};

        -- 建筑转换
        DungeonM.transformGrid(class, pos, GRID_STATE_OPEN, para);

        -- 抛出建筑转换的事件
        EventMgr.fire(event.BUILDING_TRANS_BUILDING, {["pos"] = pos, ["bonus"] = bonus, ["class"] = class,});
    else
        -- 把格子设置为失效
        grid:changeState(GRID_STATE_DISABLE);

        -- 抛出摧毁钢铁之门事件
        EventMgr.fire(event.DESTORY_STEEL_DOOR, {["pos"] = pos, ["bonus"] = bonus,});
    end

    return true;
end

-- 获取钢铁之门增益的描述
function getSteelDoorAssistInfo(target)
    local props = target.dbase:queryTemp("prop", "building_assist", {});
    if not props or #props == 0 then
        return nil;
    end

    return {
        ["name"] = getLocStr("avalon_steel_door_addition_title"),
        ["desc"] = getLocStr("avalon_steel_door_addition"),
        ["bgColor"] = "green",
    }
end

-- 计算阿瓦隆实验室的消耗
function calcAvalonLabCost(labType)
    local cost = FormulaM.invoke("CALC_AVALON_LAB_NEED", labType);

    return {["lab_cost"] = cost};
end

-- 向阿瓦隆实验室提交物品
function submitToAvalonLab(pos)
    local grid = DungeonM.getGridByPos(pos);
    if not grid then
        return false;
    end

    -- 当前格子已经无效了
    if grid.state == GRID_STATE_DISABLE then
        return false;
    end

    if grid.type ~= GRID_TYPE_AVALON_LAB then
        return false;
    end

    -- 实验室类型
    local elementId = grid.element;
    if not elementId or type(elementId) ~= "number" then
        return false;
    end

    local args = DungeonM.queryElement(elementId).args;
    if not args or type(args) ~= "table" then
        return false;
    end

    local labType = args.lab_type;
    if not labType then
        return false;
    end

    -- 获取消耗
    local cost = grid.lab_cost;

    -- 检查是否足够消耗
    if not checkCost(cost) then
        -- 抛出扣除不足事件
        EventMgr.fire(event.SUBMIT_TO_AVALON_LAB, {["isSuccess"] = false, ["pos"] = pos, ["labType"] = labType,});
        return false;
    end

    -- 扣除消耗
    if #cost > 0 then
        if cost[1] == 1 then
            -- 扣除物品
            DungeonServiceM.costDungeonItem(cost[2], cost[3]);
        elseif cost[1] == 2 then
            -- 扣除属性
            ME.user:costAttrib(cost[2], cost[3]);
        end
    end

    -- 计算奖励
    local bonus = FormulaM.invoke("CALC_AVALON_LAB_BONUS", labType);

    -- 给予奖励
    if #bonus > 0 then
        if type(bonus[1]) == "number" then
            -- 道具、属性奖励
            BonusM.doBonus(bonus, "avalon_lab");
        elseif bonus[1] == "manual" then
            -- 工程学手册奖励
            EngineeringManualM.rewardKnowledgePoint(bonus[2], bonus[3]);
        end
    end

    -- 成功了，把格子设置为失效
    grid:changeState(GRID_STATE_DISABLE);

    -- 抛出奖励事件
    EventMgr.fire(event.SUBMIT_TO_AVALON_LAB, {["isSuccess"] = true, ["pos"] = pos, ["bonus"] = bonus, ["labType"] = labType,});

    -- 通知建筑事件完成
    EventMgr.fire(event.BUILDING_EVENT_DONE, {["pos"] = pos,});

    return true;
end

-- 检查是否够消耗
function checkCost(cost)
    if #cost == 0 then
        return true;
    end

    local ok = true;
    if cost[1] == 1 then
        ok = ItemM.getAmount(ME.user, cost[2]) >= cost[3];
    elseif cost[1] == 2 then
        -- 如果扣除的是血量的话，那么必须是大于0，因为不能把玩家扣死了
        if cost[2] == "hp" then
            ok = ME.user:queryAttrib(cost[2]) > cost[3];
        else
            ok = ME.user:queryAttrib(cost[2]) >= cost[3];
        end
    end

    return ok;
end
